use axum::{
    body::Body,
    http::{Request, StatusCode},
    middleware::Next,
    response::{IntoResponse, Response},
    Json,
};
use futures_util::FutureExt;
use std::backtrace::Backtrace;
use std::panic::AssertUnwindSafe;

/// Panic 捕获中间件
pub async fn catch_panic_middleware(req: Request<Body>, next: Next) -> Response {
    let result = AssertUnwindSafe(next.run(req)).catch_unwind().await;

    match result {
        Ok(response) => response,
        Err(panic_err) => {
            let panic_msg = extract_panic_message(panic_err);
            let backtrace = Backtrace::force_capture();

            tracing::error!(
                panic_message = %panic_msg,
                backtrace = %backtrace,
                "捕获到 panic"
            );

            let filtered_backtrace = filter_backtrace(&backtrace);
            let detail = format!("Panic 消息: {}\n\n{}", panic_msg, filtered_backtrace);

            let error_response = crate::response::Response::<()>::failed_with_code(
                500,
                "服务器内部错误(panic)",
                Some(detail),
            );

            (StatusCode::OK, Json(error_response)).into_response()
        }
    }
}

/// 提取 panic 消息
fn extract_panic_message(err: Box<dyn std::any::Any + Send>) -> String {
    if let Some(s) = err.downcast_ref::<String>() {
        s.clone()
    } else if let Some(s) = err.downcast_ref::<&str>() {
        s.to_string()
    } else {
        "未知的 panic 错误".to_string()
    }
}

/// 设置全局 panic hook
pub fn setup_panic_hook() {
    std::panic::set_hook(Box::new(|panic_info| {
        let backtrace = Backtrace::force_capture();

        let location = panic_info
            .location()
            .map(|l| format!("{}:{}:{}", l.file(), l.line(), l.column()))
            .unwrap_or_else(|| "未知位置".to_string());

        let message = if let Some(s) = panic_info.payload().downcast_ref::<&str>() {
            s.to_string()
        } else if let Some(s) = panic_info.payload().downcast_ref::<String>() {
            s.clone()
        } else {
            "未知 panic".to_string()
        };

        let filtered_backtrace = filter_backtrace(&backtrace);

        tracing::error!(
            panic.location = %location,
            panic.message = %message,
            backtrace = %filtered_backtrace,
            "应用程序 panic"
        );
    }));
}

/// 过滤 backtrace,只保留业务代码
fn filter_backtrace(backtrace: &Backtrace) -> String {
    let backtrace_str = format!("{}", backtrace);

    if std::env::var("FULL_BACKTRACE").is_ok() {
        return backtrace_str;
    }

    let lines: Vec<&str> = backtrace_str.lines().collect();
    let mut filtered = Vec::new();

    for line in lines {
        let trimmed = line.trim();

        let should_keep = trimmed.contains("::src::")
            || trimmed.starts_with("at src/")
            || trimmed.starts_with("at .\\src\\")
            || trimmed.contains("\\src\\");

        let should_skip = trimmed.contains("tokio::")
            || trimmed.contains("axum::")
            || trimmed.contains("tower::")
            || trimmed.contains("hyper::")
            || trimmed.contains("std::")
            || trimmed.contains("core::")
            || trimmed.contains("alloc::")
            || trimmed.contains("rust_begin_unwind")
            || trimmed.contains("::backtrace::")
            || trimmed.starts_with("stack backtrace:");

        if should_keep && !should_skip {
            filtered.push(line);
        }
    }

    if filtered.is_empty() {
        "堆栈信息已过滤(设置环境变量 FULL_BACKTRACE=1 查看完整堆栈)".to_string()
    } else {
        format!(
            "堆栈信息(已过滤):\n{}\n\n提示: 设置环境变量 FULL_BACKTRACE=1 查看完整堆栈",
            filtered.join("\n")
        )
    }
}
